package org.intellij.doctree;

import org.intellij.doctree.node.*;

import javax.swing.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.lang.reflect.Method;

public class DocTreeTransferHandler extends TransferHandler {

    protected Transferable createTransferable(JComponent c) {
        DocTree tree = (DocTree) c;
        DocTreeNode docTreeNode = tree.getCurrentNode();
        if (docTreeNode instanceof DocumentNode) {
            return new DocumentNodeTransferable((DocumentNode) docTreeNode);
        } else if (docTreeNode instanceof FolderNode) {
            return new FolderNodeTransferable((FolderNode) docTreeNode);
        } else {
            return null;
        }
    }

    public int getSourceActions(JComponent c) {
        return COPY_OR_MOVE;
    }

    protected void exportDone(JComponent source, Transferable data, int action) {
        if (!(source instanceof DocTree)) return;
        if (action != TransferHandler.MOVE) return;
        DocTree tree = (DocTree) source;
        if (data instanceof DocTreeNodeTransferable) {
            DocTreeNode node = ((DocTreeNodeTransferable) data).getNode();
            tree.setNodeToRemove(node);
        }
    }

    public boolean importData(JComponent source, Transferable data) {
        if (source == null || (!(source instanceof DocTree))) return false;
        DocTree tree = (DocTree) source;
        DocTreeNode node = tree.getCurrentNode();
        if (node == null || !(node instanceof FolderNode)) return false;

        DataFlavor[] flavors = data.getTransferDataFlavors();
        if (importDocNode(data, flavors, tree)) return true;
        if (importFolderNode(data, flavors, tree)) return true;
        if (importText(data, flavors, tree)) return true;
        return false;

    }

    private boolean importDocNode(Transferable t, DataFlavor[] flavors, DocTree tree) {
        boolean found = false;
        for (DataFlavor flavor : flavors) {
            if (flavor == null) continue;
            if (flavor.equals(DocumentNodeTransferable.documentNodeFlavor)) found = true;
        }
        if (!found) return false;

        DocumentNode docNode;
        try {
            docNode = (DocumentNode) t.getTransferData(DocumentNodeTransferable.documentNodeFlavor);
        } catch (Exception e) {
            return false;
        }

        DocTreeComponent component = tree.getDocTreeComponent();
        DocTreeNode node = tree.getCurrentNode();
        component.addDocument((FolderNode) node, docNode.getDocument());

        DocTreeNode nodeToRemove = tree.getNodeToRemove();
        if (nodeToRemove != null && nodeToRemove.equals(docNode)) {
            component.removeNode(nodeToRemove);
        }
        tree.setNodeToRemove(null);

        return true;
    }

    private boolean importFolderNode(Transferable t, DataFlavor[] flavors, DocTree tree) {
        boolean found = false;
        for (DataFlavor flavor : flavors) {
            if (flavor == null) continue;
            if (flavor.equals(FolderNodeTransferable.folderNodeFlavor)) found = true;
        }
        if (!found) return false;

        FolderNode folderNode;
        try {
            folderNode = (FolderNode) t.getTransferData(FolderNodeTransferable.folderNodeFlavor);
        } catch (Exception e) {
            return false;
        }

        DocTreeComponent component = tree.getDocTreeComponent();
        DocTreeNode node = tree.getCurrentNode();
        component.addFolder((FolderNode) node, folderNode.getFolder());

        DocTreeNode nodeToRemove = tree.getNodeToRemove();
        if (nodeToRemove != null && nodeToRemove.equals(folderNode)) {
            component.removeNode(nodeToRemove);
        }
        tree.setNodeToRemove(null);

        return true;
    }

    private boolean importText(Transferable t, DataFlavor[] flavors, DocTree tree) {
        boolean found = false;
        for (DataFlavor flavor : flavors) {
            if (flavor == null) continue;
            if (flavor.isFlavorTextType()) found = true;
        }
        if (!found) return false;

        String url;
        try {
            url = (String) t.getTransferData(DataFlavor.stringFlavor);
        } catch (Exception e) {
            return false;
        }

        DocumentDialog dialog = new DocumentDialog(tree);
        dialog.setURL(url);
        dialog.centerRelativeToParent();
        dialog.show();

        if (dialog.isOkFlag()) {
            Document newDocument = dialog.getDocument();
            DocTreeComponent component = tree.getDocTreeComponent();
            DocTreeNode node = tree.getCurrentNode();
            component.addDocument((FolderNode) node, newDocument);
        }

        return true;
    }

    public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
        for (DataFlavor flavor : transferFlavors) {
            if (flavor.isFlavorTextType()) return true;
            if (DocumentNodeTransferable.documentNodeFlavor.equals(flavor)) return true;
            if (FolderNodeTransferable.folderNodeFlavor.equals(flavor)) return true;
        }
        return false;
    }

    /**
     * Returns the clipboard to use for cut/copy/paste.
     *
     * @param c the component
     * @return the clipboard
     */
    public Clipboard getClipboard(JComponent c) {
        // get clipboard from superclass - private method...
        try {
            Method method = TransferHandler.class.getDeclaredMethod("getClipboard", new Class[] {JComponent.class});
            method.setAccessible(true);
            return (Clipboard) method.invoke(this, new Object[] {c});
        } catch (Throwable t) {
            return null;
        }
    }

}
